package pl.edu.agh.jemo.evolution.operator.crossover.impl;

import javax.swing.JPanel;

import pl.edu.agh.jemo.evolution.common.JemoRandom;
import pl.edu.agh.jemo.evolution.genotype.Genotype;
import pl.edu.agh.jemo.evolution.operator.crossover.ClassicCrossover;
import pl.edu.agh.jemo.evolution.specimen.Specimen;
import pl.edu.agh.jemo.gui.panels.crossover.LinearCrossoverConfigPanel;

/**
 * Specimen generated as a result by this class, are linear combination of
 * parent specimen. It means, that in graphical representation of specimen,
 * children will always lie on the straight line between parents, or on this
 * line casted to one of dimensions axis. Result of this crossover is also
 * simetric, so the distance between one new specimen and one parent is equal to
 * distance between another specimen and it's parent. This crossover uses
 * ClassicCrossover methods for choice of specimen and their processing after
 * recombination is done.
 * 
 * 
 * @author Marcin Zbijowski
 * 
 */
public class LinearCrossover extends ClassicCrossover {
	/**
	 * Percent of dimensions of genotype, that will be affected by crossover.
	 */
	protected Double dimensionsToCross = .5;

	/**
	 * Generates two children based on given parent specimen.
	 * Generates random split point and for each dimension, cross chance is tested. If test succeds exchange genotypes in place selected by splitPoint.
	 * As a result new specimen should lie on a line between parents, or this line casted on anaffected dimension axis.
	 * 
	 * @param first First parent specimen reference.
	 * @param second Second parent specimen reference.
	 * @return Array containing two child specimen references. Child referece points at new instances of specimen, so given specimen should remain unchanged.
	 */
	@Override
	protected Specimen[] recombinate(Specimen first, Specimen second) {
		Specimen child1 = first.clone();
		Specimen child2 = second.clone();
		Genotype[] child1Genotype = child1.getGenotype();
		Genotype[] child2Genotype = child2.getGenotype();		
		double splitPoint = JemoRandom.getRandom().nextDouble();

		for (int i = 0; i < child1Genotype.length ; i++) {
			if (JemoRandom.getRandom().nextDouble() < dimensionsToCross) {
				Double temp = child1Genotype[i].asDouble() * splitPoint + child2Genotype[i].asDouble() * (1-splitPoint);
				child2Genotype[i].fromDouble( child1Genotype[i].asDouble() * (1 - splitPoint) + child2Genotype[i].asDouble() * splitPoint);
				child1Genotype[i].fromDouble( temp );
			}
		}
		child1.calculatePhenotype();
		child2.calculatePhenotype();
		Specimen[] result = new Specimen[2];
		result[0] = child1;
		result[1] = child2;
		return result;
	}

	/**
	 * Returns chance to cross single dimension.
	 * 
	 * @return percent chance to modify child specimen on a given dimension.
	 */
	public Double getDimensionsToCross() {
		return dimensionsToCross;
	}

	/**
	 * Sets new chance to modify genotype on single dimension parameter.
	 * 
	 * @param dimensionsToCross new dimensionToCross parameter value.
	 */
	public void setDimensionsToCross(Double dimensionsToCross) {
		this.dimensionsToCross = dimensionsToCross;
	}

	/**
	 * Returns configuration panel being extension of JPanel instance.
	 * Returned panel is displayed by graphical user interface allowing user to confgiure crossover.
	 * This method may return null if no configuration for operator is possible.
	 * 
	 * @return Instance of JPanel with initialized components prepared to configure objects properties.
	 */
	@Override
	public JPanel getConfigPanel() {
		return new LinearCrossoverConfigPanel(this);
	}
	
	/**
	 * Returns String object containing description of crossover configuration in readable form as a summary.
	 * 
	 * @return String description of object configuration.
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(super.toString());
		sb.append("Dimensions to cross: " + dimensionsToCross + "\n");
		return sb.toString();
	}
}
